//! 身份证号解析
//! 
//! `data.rs` 行政区划代码表，因总量较少不区分省、市、区代码层级使用 `BigTable` 简化维护成本，提升未来扩展性 
//! 
//! `data.rs` 由 `build.rs` 自动获取更新 `中华人民共和国县级以上行政区划代码` 生成，因此无需手动维护。

use std::collections::HashMap;
use once_cell::sync::Lazy;

static DATES_TABLE: Lazy<HashMap<u32, &str>> = Lazy::new(|| include!("data.rs").into_iter().copied().collect());

pub fn check_id_card(id_card_no: &str) -> String {
    let mut result = "身份证号码正确".to_string();
    let (sex, year, month, day): (u8, String, &str, &str);
    match id_card_no.len() {
        15 => {
            sex = id_card_no[14..]
                .chars()
                .next()
                .unwrap()
                .to_digit(10)
                .unwrap() as u8;
            year = "19".to_string() + &id_card_no[6..8];
            month = &id_card_no[8..10];
            day = &id_card_no[10..12];
        }
        18 => {
            let id_card_no18 = id_card_no.chars().last().unwrap();
            if !check_check_code(&id_card_no[..17], id_card_no18) {
                return "身份证号码错误".to_string();
            }
            sex = id_card_no[16..17]
                .chars()
                .next()
                .unwrap()
                .to_digit(10)
                .unwrap() as u8;
            year = id_card_no[6..10].to_string();
            month = &id_card_no[10..12];
            day = &id_card_no[12..14];
        }
        _ => return "身份证号码错误".to_string(),
    }

    let sex = read_sex(sex);
    result = format!("{},{}", result, sex);

    if !check_date(year.as_str(), month, day) {
        return "身份证号码错误".to_string();
    }
    result = format!("{},{}年{}月{}日", result, year, month, day);

    let area = read_area(&id_card_no[0..6]);
    if area.len() == 0 {
        return "身份证号码错误".to_string();
    }
    result = format!("{},{}", result, area);

    result
}

/// 检查身份证号日期合法性
#[inline(always)]
fn check_date(year: &str, month: &str, day: &str) -> bool {
    let year = year.parse::<u16>().unwrap();
    let month = month.parse::<u8>().unwrap();
    let day = day.parse::<u8>().unwrap();
    if !(year >= 1900 && year <= 2100) {
        return false;
    }
    match month {
        1 | 3 | 5 | 7 | 8 | 10 | 12 if day <= 31 => true,
        4 | 6 | 9 | 11 if day <= 30 => true,
        2 => match (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) {
            true if day <= 29 => true,
            false if day <= 28 => true,
            _ => false,
        },
        _ => false,
    }
}

/// 读取身份证号中性别信息
#[inline(always)]
fn read_sex(id_card_no_sex: u8) -> String {
    match id_card_no_sex % 2 == 0 {
        true => "女".to_string(),
        false => "男".to_string(),
    }
}

/// 检查身份证号校验码
#[inline(always)]
fn check_check_code(id_card_no17: &str, id_card_no18: char) -> bool {
    const WI: [u8; 17] = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
    let mut tmp = 0;
    for (i, c) in id_card_no17.char_indices() {
        tmp += WI[i] * c.to_digit(10).unwrap() as u8 % 11
    }
    const CHECK_CODE: [char; 11] = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
    CHECK_CODE[(tmp % 11) as usize] == id_card_no18
}

/// 读取身份证号地区
#[inline(always)]
fn read_area(id_card_no_area: &str) -> String {
    let mut area = "".to_string();
    if let Some(province) = DATES_TABLE.get(&id_card_no_area[0..2].parse::<u32>().unwrap()) {
        area = province.to_string();
    } else {
        return area;
    }
    if let Some(city) = DATES_TABLE.get(&id_card_no_area[0..4].parse::<u32>().unwrap()) {
        area = format!("{}-{}", area, city);
    }
    if let Some(district) = DATES_TABLE.get(&id_card_no_area.parse::<u32>().unwrap()) {
        area = format!("{}-{}", area, district);
    }
    area
}
